Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement VRF route leaking #160

Merged
merged 7 commits into from
Jul 19, 2024
Merged

Implement VRF route leaking #160

merged 7 commits into from
Jul 19, 2024

Conversation

fedepaol
Copy link
Member

@fedepaol fedepaol commented Jun 21, 2024

Is this a BUG FIX or a FEATURE ?:

Uncomment only one, leave it on its own line:

/kind bug
/kind cleanup
/kind feature
/kind design
/kind flake
/kind failing
/kind documentation
/kind regression

What this PR does / why we need it:

By implementing import VRF, we allow routes defined in the RIB of a router related to a given VRF to be imported from another router tied to another VRF.
This is true for routes being received and being announced.
In case of routes being announced, we still limit the routes to all those defined in the "allowed outgoing" section of a given neighbor.

Special notes for your reviewer:

Release note:

Implement the import vrf feature as described in [https://docs.frrouting.org/en/latest/bgp.html#clicmd-import-vrf-VRFNAME](https://docs.frrouting.org/en/latest/bgp.html#clicmd-import-vrf-VRFNAME). By implementing import VRF, we allow routes defined in the RIB of a router related to a given VRF to be imported from another router tied to another VRF.

@fedepaol fedepaol force-pushed the leakvrf branch 7 times, most recently from 22ac9f8 to f74e335 Compare June 28, 2024 12:26
@fedepaol fedepaol changed the title WIP: Implement VRF route leaking Implement VRF route leaking Jun 28, 2024
@fedepaol fedepaol force-pushed the leakvrf branch 7 times, most recently from 2e85fd6 to bee5ac2 Compare June 29, 2024 19:05
func validatePrefixes(prefixes []string) error {
for _, p := range prefixes {
family := ipfamily.ForCIDRString(p)
if family == ipfamily.Unknown {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: ipfamily.ForCIDRString(p) ==ipfamily.Unknown just because family word is occuring too many times

@@ -67,8 +67,37 @@ func apiToFRR(resources ClusterResources, alwaysBlock []net.IPNet) (*frr.Config,
}

alwaysBlockFRR := alwaysBlockToFRR(alwaysBlock)
prefixesForVRF := map[string][]string{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: that could be a type X with func newX(routers) X and methods X.validate()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's "that"? the whole map or []string{} ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and yes, I agree it's borderline to be readeable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I mean the whole map[string][]string but as is also ok

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left the type unchanged but hid the logic behind a couple of functions. Hoping it's readable. I don't feel hiding the map behind a type would be beneficial here.

}

// All prefixes available in this router: defined and imported from VRFs
prefixesInRouter := sets.New(r.Prefixes...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why pick sets and not using standard library map?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I like sticking to the stdlib, sets are more convenient to get the output ordered (which we want, so the generated configuration is stable).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the sort the only reason?
but we already have maps for bfd, and sort function in the code base, therefore this introduce different approach

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also use sets already

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no I think you are right, here we don't even leverage the order. Let me switch to maps

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or not !!! we rely on the order, reverting to set

@@ -384,6 +400,32 @@ func validateSelectorLengths(mask int, le, ge uint32) error {
return nil
}

func validateImportVRFs(r v1beta1.Router, allVRFs map[string][]string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to have go coverage to see how much of this code is being unit-testes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can file an issue to add that, or do you think it relates to this PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put random panics and unit-tests are still green, the error branch looks not really unit-tested. But a test coverage would give us precise what we miss.

git diff
diff --git a/internal/controller/api_to_config.go b/internal/controller/api_to_config.go
index 1b0f7a3..d166f9b 100644
--- a/internal/controller/api_to_config.go
+++ b/internal/controller/api_to_config.go
@@ -403,9 +403,15 @@ func validateSelectorLengths(mask int, le, ge uint32) error {
 func validateImportVRFs(r v1beta1.Router, allVRFs map[string][]string) error {
        for _, i := range r.Imports {
                if i.VRF == "default" {
+                       if true {
+                               panic("")
+                       }
                        continue
                }
                if _, ok := allVRFs[i.VRF]; !ok {
+                       if true {
+                               panic("")
+                       }
                        return fmt.Errorf("router %d-%s imports vrf %s which is not defined", r.ASN, r.VRF, i.VRF)
                }
        }
@@ -619,6 +625,9 @@ func validatePrefixes(prefixes []string) error {
        for _, p := range prefixes {
                family := ipfamily.ForCIDRString(p)
                if family == ipfamily.Unknown {
+                       if true {
+                               panic("")
+                       }
                        return fmt.Errorf("unknown ipfamily for %s", p)
                }
        }
[I] kka@f-t14s ~/w/g/m/frr-k8s (leakvrf ✖1)> go test ./internal/controller/...  -run=TestConversion/ -count=1
ok      github.com/metallb/frr-k8s/internal/controller  0.023s

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, I understand we are not covering, I am fine with extending but I don't want to add the coverage logic in CI here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not asking to add coverage ci logic, I am only asking if you can see the coverage once locally in your dev machine. The real ask is if you think we should more unit-test and cover allo the error branches that are not covered.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think you covered a bit more in the latest push)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this done (curious though if you can run locally in your dev a coverage, I cannot due to the fact that controller unit test needs envtest way of running)

}
for _, p := range n.ToAdvertise.Allowed.Prefixes {
if !prefixesInRouter.Has(p) {
return fmt.Errorf("trying to advertise non configured prefix %s to neighbor %s, vrf %s", p, neighborName(n.ASN, n.Address), routerConfig.VRF)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log line could split the args into newline.
Also why do we need to error here? I mean that config might makes sense and allow user to filter out per neighbor in the allow prefix? Or is the FRR drops an error?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we allow to advertise a given prefix to a neighbor. We want to tell the user, he thinks he's allowing a prefix that can't come from anywhere as it's not in the router's prefix or in any imported route from other vrfs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay I got it the otherway around :) that might be a feedback. Can I have in allow a prefix a /16 and overtime to add remove /24?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what do you mean by overtime. In general, with allow you must match something you have in hte prefix list (or you can have all).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I mean on day one user sets on the default vrf the allow prefix list to 10.10.0.0/16, and in day X user adds the prefix in vrf red 10.10.10.0/24 (which is imported) and in day X+1 user in green the prefix 10.10.20.0/24 (which is imported). Is that a use case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not exactly, as each configuration is meant to be self - contained. So, when you say default imports red, it really means import all the learned routes from red + advertise the prefixes defined locally in the stanza belonging to the red vrf.

},
err: nil,
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go test ./internal/controller/...  -run=TestConversion/Multiple_Routers_import -v
=== RUN   TestConversion
=== RUN   TestConversion/Multiple_Routers_import_VRFs
=== RUN   TestConversion/Multiple_Routers_import_VRF,_advertise_ips_from_the_imported_vrf
=== RUN   TestConversion/Multiple_Routers_import_VRFs,_advertise
--- PASS: TestConversion (0.01s)
    --- PASS: TestConversion/Multiple_Routers_import_VRFs (0.00s)
    --- PASS: TestConversion/Multiple_Routers_import_VRF,_advertise_ips_from_the_imported_vrf (0.00s)
    --- PASS: TestConversion/Multiple_Routers_import_VRFs,_advertise (0.00s)
PASS

I cannot tell the diff on the test by looking the description

Unless I am lost in the big file the first and third is the same, and probably the second is superset of the fist

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it is:

--- PASS: TestConversion/Multiple_Routers_import_VRFs (0.00s)
--- PASS: TestConversion/Multiple_Routers_import_VRF,_advertise_ips_from_the_imported_vrf (0.00s)
--- PASS: TestConversion/Multiple_Routers_import_non_existing_VRFs (0.00s)
--- PASS: TestConversion/Multiple_Routers_import_VRF,_red_imports_default_and_advertises (0.00s)

first is only import with no advertisement. The ones with advertisements are supersets, but I think it still make sense to have them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

// All prefixes available in this router: defined and imported from VRFs
prefixesInRouter := sets.New(r.Prefixes...)
for _, i := range r.Imports {
importedPrefixes := prefixesForVRF[i.VRF]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is confusing e.g. assigning when the key is not there.
Maybe it be something like

key=i.VRF
if i.VRF="default" {key=""}
importePrefixes = prefixesForVRF[key]
  1. what happens if key does exist, should we check if v,ok:= prefixesForVRF[key];!ok{}
  2. can we eliminate the transform from default? (maybe when create the original the "" to be "default")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the problem here is, frr wants "default" when importing, while in the api is cleaner not to bother about "default" VRF if you don't have to deal with VRFs at all. Because of that, I couldn't find a better way to do this translation. I think having a slimmer api has more value over this.

Re - key not existing I need to recheck.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll find a way to make it clearer. The missing key can't happen because we validate that imported vrfs exist and we fill a record for each vrf, but I agree it's weak and we should at least log (or even better panic) if the key is not there as we think it cna't happen.

if i.VRF == "default" {
importedPrefixes = prefixesForVRF[""]
}
prefixesInRouter.Insert(importedPrefixes...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the behavior if two vrfs have same prefix?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll have the same prefix twice which won't be a problem as they are used to understand what we can / can't advertise

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it one or two lines in the final config? (this can be test entry)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually not, it's a set. So they'll be squashed

ip prefix-list 192.168.1.2-pl-ipv4 seq 1 deny any
ipv6 prefix-list 192.168.1.2-pl-ipv4 seq 2 deny any


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have a golden file without extra empty lines?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the problem is not related to the golden file but how the templates are generated. We need to tweak the "-" in the go template to reduce them, but it's not a fight that makes sense to fight in this PR.
I'll file an issue to track it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good for me

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filed #170

res := []string{}
for _, p := range prefixes {
family := ForCIDRString(p)
if family != familyToFilter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if ForCIDRString(p)!=familyFilter

}
for _, p := range n.ToAdvertise.Allowed.Prefixes {
if !prefixesInRouter.Has(p) {
return fmt.Errorf("trying to advertise non configured prefix %s to neighbor %s, vrf %s", p, neighborName(n.ASN, n.Address), routerConfig.VRF)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I mean on day one user sets on the default vrf the allow prefix list to 10.10.0.0/16, and in day X user adds the prefix in vrf red 10.10.10.0/24 (which is imported) and in day X+1 user in green the prefix 10.10.20.0/24 (which is imported). Is that a use case?

@@ -384,6 +400,32 @@ func validateSelectorLengths(mask int, le, ge uint32) error {
return nil
}

func validateImportVRFs(r v1beta1.Router, allVRFs map[string][]string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not asking to add coverage ci logic, I am only asking if you can see the coverage once locally in your dev machine. The real ask is if you think we should more unit-test and cover allo the error branches that are not covered.

return nil, err
}

routerPrefixes, err := importedPrefixes(r, routersPrefixes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having routerPrefixes and routersPrefixes and many Prefixes makes the code less readable imo.
Maybe instead localPrefixes, allPrefixes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also merge the importedPrefixes and validateImportVRFs and rename the function prefixesToImport

local, err: =getLocalPrefixesIfValid()
imported,err:=getPrefixToImportIfValid()
local=append(local, imported...)

Names are first random thought while reading the code

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also merge the importedPrefixes and validateImportVRFs and rename the function prefixesToImport

local, err: =getLocalPrefixesIfValid()
imported,err:=getPrefixToImportIfValid()
local=append(local, imported...)

Names are first random thought while reading the code

This is quite messy already, and I'd rather keep the validation separate so it's clear we are not hiding much behind the courtains.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having routerPrefixes and routersPrefixes and many Prefixes makes the code less readable imo. Maybe instead localPrefixes, allPrefixes

let me try to find a better name

@@ -384,6 +400,32 @@ func validateSelectorLengths(mask int, le, ge uint32) error {
return nil
}

func validateImportVRFs(r v1beta1.Router, allVRFs map[string][]string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think you covered a bit more in the latest push)

},
err: nil,
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -2493,7 +2767,7 @@ func TestConversion(t *testing.T) {
if test.err == nil && err != nil {
t.Fatalf("expected no error, got %v", err)
}
if diff := cmp.Diff(frr, test.expected); diff != "" {
if diff := cmp.Diff(frr, test.expected, cmpopts.EquateEmpty()); diff != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is EquateEmpty() needs to be in this commit or
in Conversion unit tests: remove the empty fields from expected commit? (I do not mind staying here if the answer no)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added as part of this test addition, then I filed a commit for cleaning up the others. I could've gone the other way around (or add this with the extra fields and then clean those in a later commit). Didn't seem too bad :P

@@ -4,7 +4,6 @@ package routes

import (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the commit message:
is there a them for the commit? like : word before : can be E2E or API, or Controller ?
maybe E2E: description

note: I did not understood that flag means the return bool value, is it valid term?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this done

@@ -0,0 +1,32 @@
// SPDX-License-Identifier:Apache-2.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general the pattern seems to E2E:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what pattern?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I have been missing the comments that I need to reword before submit the code review.
I am talking about the commit message it has E2E Tests: test, the pattern in previous commits is only E2E:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this


defer ginkgo.GinkgoRecover()
updater, err := config.NewUpdater()
Expect(err).NotTo(HaveOccurred())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general assign stuff in the tree construction phase is not right
https://onsi.github.io/ginkgo/#mental-model-how-ginkgo-traverses-the-spec-hierarchy

var _ = ginkgo.Describe("Leaked routes with import vrfs should work", func() {
var cs clientset.Interface

defer ginkgo.GinkgoRecover()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you know why this is needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably copy / pasted. Let me remove it and see how it goes.

@@ -0,0 +1,32 @@
// SPDX-License-Identifier:Apache-2.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I have been missing the comments that I need to reword before submit the code review.
I am talking about the commit message it has E2E Tests: test, the pattern in previous commits is only E2E:


updateAndCheckPeered(*config, peersDefault, peersVRF, frrsDefault, frrsVRF, ipFamily)

ginkgo.By("validating")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this description does add real value

Copy link
Member Author

@fedepaol fedepaol Jul 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jokes aside, I like seeing a test split in the setup / validation and the By makes both easier to understand while the test is running and when we read it.

It makes the test go from big silence to "I am doing this right now" to knowing where it is, especially when running locally.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In well formed test the BeforeEach/JustBeforeEach is what should be before validating (but table driven kind of make it hard properly form the test because the Before each must have a specific to entry).

Imo running locally the test, you can have prints/delve/It to show the progress.

We can pick it in the issue so no action here

frrsDefault, peersDefault, neighborsDefault := initNeighbors(false, ipFamily)
frrsVRF, peersVRF, neighborsVRF := initNeighbors(true, ipFamily)

ginkgo.By("pairing with nodes")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be omitted

frrsVRF, peersVRF, neighborsVRF := initNeighbors(true, ipFamily)

ginkgo.By("pairing with nodes")
pairWithNodes(frrsDefault, ipFamily, toAdvertise)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer is all ginkgo asser takes place not nested == function to return error and assert here


}

updateAndCheckPeered(*config, peersDefault, peersVRF, frrsDefault, frrsVRF, ipFamily)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing *config as argument does not look good, is it required?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not? If the function accepts a value and doesn't expect to modify it, it should accept a pointer. On the other hand, I need to use deepcopy to be able to leverage baseConfig, and that returns a pointer. At some point in the stack I need to reference it. I will do when I get the deepcopy, so it's nicer.

}
config.Spec.BGP.Routers[0].Neighbors[i].ToReceive.Allowed.Mode = allowMode
}
for i := range config.Spec.BGP.Routers[1].Neighbors {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you use range to get the index, imo the thing should work

for _,n:=range xxx{
do something with n
}

If it is only to use the array itself a for loop is cleaner.

But the effort to make the first to work (pointers/nested structs etc) might not worth, and therefore keeping as is works for me

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if "do something with n" is actually changing n, range won't work

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true because https://github.com/metallb/frr-k8s/blob/main/api/v1beta1/frrconfiguration_types.go#L78 is not an array of pointers, was that a design decision?

(no action needed)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why it would be a better design decision for the api?

@fedepaol fedepaol force-pushed the leakvrf branch 2 times, most recently from 906e0c6 to d1926fa Compare July 18, 2024 15:16
@@ -4,7 +4,6 @@ package routes

import (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this done

}
config.Spec.BGP.Routers[0].Neighbors[i].ToReceive.Allowed.Mode = allowMode
}
for i := range config.Spec.BGP.Routers[1].Neighbors {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true because https://github.com/metallb/frr-k8s/blob/main/api/v1beta1/frrconfiguration_types.go#L78 is not an array of pointers, was that a design decision?

(no action needed)


updateAndCheckPeered(*config, peersDefault, peersVRF, frrsDefault, frrsVRF, ipFamily)

ginkgo.By("validating")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In well formed test the BeforeEach/JustBeforeEach is what should be before validating (but table driven kind of make it hard properly form the test because the Before each must have a specific to entry).

Imo running locally the test, you can have prints/delve/It to show the progress.

We can pick it in the issue so no action here

@@ -384,6 +400,32 @@ func validateSelectorLengths(mask int, le, ge uint32) error {
return nil
}

func validateImportVRFs(r v1beta1.Router, allVRFs map[string][]string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this done (curious though if you can run locally in your dev a coverage, I cannot due to the fact that controller unit test needs envtest way of running)

@@ -79,6 +79,16 @@ type Router struct {
// Prefixes is the list of prefixes we want to advertise from this router instance.
// +optional
Prefixes []string `json:"prefixes,omitempty"`

// Imports is the list of imports we want for this router / vrf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imports is the list of imports, could be more readable to like is a list of VRFs from which to import their prefixes?

Here we add the simplest way to route from a VRF to the other, by using
import vrf <vrfname>, which is a shortcut to leak routes from a vrf to
the other using the vpn SAFI under the default VRF.

Also, despite the fact that the import goes in the ipfamily stanza, we
choose here to allow an import that includes both the v4 and the v6
family.

Signed-off-by: Federico Paolinelli <[email protected]>
We introduce a new importVRFs field to list the vrfs imported to the v4
/ v6 AFIs of a given router.

Also, we implement the rendering of that field in the frr config.

Signed-off-by: Federico Paolinelli <[email protected]>
Translating and merging the new field.

Signed-off-by: Federico Paolinelli <[email protected]>
Instead of relying on the returned bool, we propagate the error and we assert on
the error routes not found when we know the routes shouldn't be there.
This makes triaging easier as the error is more descriptive than a bool.

Signed-off-by: Federico Paolinelli <[email protected]>
Here we test leaking routes from / to VRFs with different types of
allowed prefixes.

Signed-off-by: Federico Paolinelli <[email protected]>
Now that we use EquateEmpty, the expected value can be shorter and more
compact, making the tests easier to navigate and handle.

Signed-off-by: Federico Paolinelli <[email protected]>
Leaking advertised networks doesn't work in FRR 9.1, so we disable the
test. We will re-enable them once the CI is stable with FRR 10.

Signed-off-by: Federico Paolinelli <[email protected]>
@fedepaol fedepaol merged commit 30da0a4 into metallb:main Jul 19, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants